             package com.java.diagnostics.visualizer.gc.i5V6r1.parser;
             
             import com.java.diagnostics.visualizer.data.DataLevel;
             import com.java.diagnostics.visualizer.data.SourceData;
             import com.java.diagnostics.visualizer.data.TupleData;
             import com.java.diagnostics.visualizer.data.TupleDataBuilder;
             import com.java.diagnostics.visualizer.data.axes.AxisPair;
             import com.java.diagnostics.visualizer.data.axes.XDataAxis;
             import com.java.diagnostics.visualizer.data.axes.YDataAxis;
             import com.java.diagnostics.visualizer.exceptions.GCAndMemoryVisualizerException;
             import com.java.diagnostics.visualizer.factory.DataFactory;
             import com.java.diagnostics.visualizer.factory.SourceDataFactory;
             import com.java.diagnostics.visualizer.gc.i5classic.parser.InstanceI5ClassicParser;
             import com.java.diagnostics.visualizer.gc.i5support.util.Messages;
             import com.java.diagnostics.visualizer.impl.factory.LogFactory;
             import com.java.diagnostics.visualizer.metadata.TupleMetaData;
             import com.java.diagnostics.visualizer.metadata.TupleMetaData.TableField;
             import com.java.diagnostics.visualizer.metadata.TupleMetaData.TableType;
             import com.java.diagnostics.visualizer.parser.vgc.VGCPlainTextParser;
             import com.java.diagnostics.visualizer.parser.vgc.constants.VGCAxes;
             import com.java.diagnostics.visualizer.parser.vgc.converters.GCNumberConverter;
             import com.java.diagnostics.visualizer.parsers.Parser;
             import com.java.diagnostics.visualizer.properties.OutputProperties;
             import com.java.diagnostics.visualizer.sources.Source;
             import java.io.BufferedReader;
             import java.io.IOException;
             import java.io.InputStreamReader;
             import java.io.PrintWriter;
             import java.io.StringWriter;
             import java.util.ArrayList;
             import java.util.Calendar;
             import java.util.HashMap;
             import java.util.logging.Level;
             import java.util.logging.Logger;
             import java.util.regex.Matcher;
             import java.util.regex.Pattern;
             
             public class InstanceI5V6r1Parser
               extends VGCPlainTextParser
             {
               private static final Pattern DATE_PATTERN = Pattern.compile("([0-9][0-9])/([0-9][0-9])/([0-9][0-9]) ([0-9][0-9]):([0-9][0-9]):([0-9][0-9]).([0-9][0-9][0-9])");
               
               private static final SourceDataFactory SOURCE_DATA_FACTORY = SourceDataFactory.getFactory();
               private final Logger LOGGER = LogFactory.getTrace(Parser.class);
               
               private static final Logger TRACE = LogFactory.getTrace(InstanceI5ClassicParser.class);
               
               private boolean absoluteTime = false;
               private HashMap idTable;
               private ArrayList chartData;
               private int chartDataIndex = 0;
               private int lowestCycleNumber = 2147483647;
               private int lowestCycleNumberEntry = 0;
               
               private long currentTime;
               
               private SourceData sourceData;
               
               private XDataAxis xAxis;
               private GCNumberConverter gcNumberConverter;
               private YDataAxis heapAxis;
               private YDataAxis numberAxis;
               private YDataAxis timeAxis;
               private TupleDataBuilder softReferences;
               private TupleDataBuilder weakReferences;
               private TupleDataBuilder finalReferences;
               private TupleDataBuilder phantomReferences;
               private TupleDataBuilder totalTimes;
               private TupleDataBuilder markTimes;
               private TupleDataBuilder sweepTimes;
               private TupleDataBuilder compactTimes;
               private TupleDataBuilder usedHeap;
               private TupleDataBuilder freeHeap;
               private TupleDataBuilder heapSize;
               private TupleDataBuilder unloadedClasses;
               private TupleDataBuilder freeLOABefore;
               private TupleDataBuilder freeLOAAfter;
               private TupleDataBuilder freeSOABefore;
               private TupleDataBuilder freeSOAAfter;
               private TupleDataBuilder totalLOABefore;
               private TupleDataBuilder totalSOABefore;
               private TupleDataBuilder totalLOAAfter;
               private TupleDataBuilder totalSOAAfter;
               private TupleDataBuilder gcInterval;
               
               protected boolean isTimeStampRelative()
               {
                 return false;
               }
               
               public SourceData parse(Source source, OutputProperties properties)
                 throws GCAndMemoryVisualizerException
               {
                 TRACE.entering(this.className, "parse");
                 
                 this.idTable = initIdTable();
                 
                 createTuples(source, properties);
                 
                 try
                 {
                   String variantIdentifier = source.getVariantIdentifier();
                   this.sourceData = 
                     SOURCE_DATA_FACTORY.createSourceData(variantIdentifier);
                   
                   trace("Ready to open input");
                   BufferedReader reader = new BufferedReader(new InputStreamReader(
                     source.getStream()));
                   
                   String firstLine = reader.readLine();
                   if (firstLine != null) {
                     firstLine = firstLine.trim();
                     if (firstLine.startsWith("OPTION ABSOLUTE_TIME")) {
                       this.absoluteTime = true;
                       trace("Absolute time specified");
                     }
                   }
                   
                   this.chartData = new ArrayList(300);
                   
                   long lastTime = 0L;
                   while (reader.ready()) {
                     trace("Ready to read an entry");
                     
                     HashMap entryData = parseEntry(reader, properties);
                     this.chartData.add(entryData);
                     if (entryData.containsKey(":Cycle")) {
                       int cycleNum = Integer.parseInt(
                         (String)entryData.get(":Cycle"));
                       if (cycleNum < this.lowestCycleNumber) {
                         this.lowestCycleNumber = cycleNum;
                         this.lowestCycleNumberEntry = this.chartDataIndex;
                       }
                     }
                     if (tracing()) {
                       trace("Entry " + this.chartDataIndex + " parsed");
                     }
                     this.chartDataIndex += 1;
                   }
                   int nextEntry = this.lowestCycleNumberEntry;
                   do {
                     HashMap entryData = (HashMap)this.chartData.get(nextEntry);
                     
                     if (entryData.containsKey("Time:CycleStart")) {
                       this.currentTime = Long.parseLong(
                         (String)entryData.get("Time:CycleStart"));
                     } else {
                       TRACE.warning("Error: Could not find Time:CycleStart");
                     }
                     if (lastTime == 0L) {
                       lastTime = this.currentTime;
                     }
                     this.xAxis.setX(this.currentTime);
                     
                     if (entryData.containsKey(":Cycle"))
                     {
                       this.gcNumberConverter.addPoint(this.xAxis.getSequenceUID(), 
                         Integer.parseInt((String)entryData.get(":Cycle")));
                     }
                     long interval = this.currentTime - lastTime;
                     addDataPoint(this.gcInterval, interval);
                     conditionallyAddNumericPointK(this.freeSOABefore, entryData, 
                       "Small:FreeStart");
                     conditionallyAddNumericPointK(this.freeSOAAfter, entryData, 
                       "Small:FreeEnd");
                     conditionallyAddNumericPointK(this.totalSOABefore, entryData, 
                       "Small:TotalStart");
                     conditionallyAddNumericPointK(this.totalSOAAfter, entryData, 
                       "Small:TotalEnd");
                     
                     conditionallyAddNumericPointK(this.freeLOABefore, entryData, 
                       "Large:FreeStart");
                     conditionallyAddNumericPointK(this.freeLOAAfter, entryData, 
                       "Large:FreeEnd");
                     conditionallyAddNumericPointK(this.totalLOABefore, entryData, 
                       "Large:TotalStart");
                     conditionallyAddNumericPointK(this.totalLOAAfter, entryData, 
                       "Large:TotalEnd");
                     conditionallyAddNumericPoint(this.unloadedClasses, entryData, 
                       "Basic:ClassesUnloaded");
                     conditionallyAddNumericPoint(this.softReferences, entryData, 
                       "Soft:Cleared");
                     conditionallyAddNumericPoint(this.weakReferences, entryData, 
                       "Weak:Cleared");
                     conditionallyAddNumericPoint(this.finalReferences, entryData, 
                       "Finalizer:Cleared");
                     conditionallyAddNumericPoint(this.phantomReferences, entryData, 
                       "Phantom:Cleared");
                     conditionallyAddNumericPoint(this.totalTimes, entryData, 
                       "Duration:Total");
                     conditionallyAddNumericPoint(this.markTimes, entryData, 
                       "Duration:Mark");
                     conditionallyAddNumericPoint(this.sweepTimes, entryData, 
                       "Duration:Sweep");
                     conditionallyAddNumericPoint(this.compactTimes, entryData, 
                       "Duration:Compact");
                     conditionallyAddNumericPointK(this.usedHeap, entryData, 
                       "Tenured:AllocatedEnd");
                     conditionallyAddNumericPointK(this.freeHeap, entryData, 
                       "Tenured:FreeEnd");
                     conditionallyAddNumericPointK(this.heapSize, entryData, 
                       "Tenured:TotalStart");
                     lastTime = this.currentTime;
                     trace("Finished one entry");
                     nextEntry++;
                     if (nextEntry >= this.chartDataIndex) {
                       nextEntry = 0;
                     }
                   } while (nextEntry != this.lowestCycleNumberEntry);
                   
                   this.sourceData.addData(this.gcInterval, DataLevel.VARIANT);
                   this.sourceData.addData(this.freeSOABefore, DataLevel.VARIANT);
                   this.sourceData.addData(this.freeSOAAfter, DataLevel.VARIANT);
                   this.sourceData.addData(this.totalSOABefore, DataLevel.VARIANT);
                   this.sourceData.addData(this.totalSOAAfter, DataLevel.VARIANT);
                   this.sourceData.addData(this.freeLOABefore, DataLevel.VARIANT);
                   this.sourceData.addData(this.freeLOAAfter, DataLevel.VARIANT);
                   this.sourceData.addData(this.totalLOABefore, DataLevel.VARIANT);
                   this.sourceData.addData(this.totalLOAAfter, DataLevel.VARIANT);
                   this.sourceData.addData(this.unloadedClasses, DataLevel.VARIANT);
                   this.sourceData.addData(this.softReferences, DataLevel.VARIANT);
                   this.sourceData.addData(this.weakReferences, DataLevel.VARIANT);
                   this.sourceData.addData(this.finalReferences, DataLevel.VARIANT);
                   this.sourceData.addData(this.phantomReferences, DataLevel.VARIANT);
                   this.sourceData.addData(this.totalTimes, DataLevel.VARIANT);
                   this.sourceData.addData(this.markTimes, DataLevel.VARIANT);
                   this.sourceData.addData(this.sweepTimes, DataLevel.VARIANT);
                   this.sourceData.addData(this.compactTimes, DataLevel.VARIANT);
                   
                   this.sourceData.addData(this.freeHeap, DataLevel.VARIANT);
                   this.sourceData.addData(this.heapSize, DataLevel.VARIANT);
                   
                   if (!this.absoluteTime) {
                     this.xAxis.updateNormalisation(this.sourceData);
                   }
                 } catch (GCAndMemoryVisualizerException ex) {
                   throw ex;
                 } catch (IOException ex) {
                   this.LOGGER.log(Level.SEVERE, " -- IOException while parsing source: ", 
                     ex);
                   throw new GCAndMemoryVisualizerException(ex);
                 } catch (Throwable ex) {
                   this.LOGGER.log(Level.SEVERE, " -- Exception while parsing source: ", 
                     ex);
                   StringWriter stringWriter = new StringWriter();
                   PrintWriter printWriter = new PrintWriter(stringWriter);
                   ex.printStackTrace(printWriter);
                   throw new GCAndMemoryVisualizerException("Caught:\n" + 
                     stringWriter.toString());
                 }
                 TRACE.exiting(this.className, "parse");
                 return this.sourceData;
               }
               
               private void conditionallyAddNumericPoint(TupleData tupleData, HashMap entryData, String id)
               {
                 if (entryData.containsKey(id)) {
                   addNumericPoint(tupleData, entryData.get(id));
                 } else {
                   TRACE.warning("ID not found:" + id);
                 }
               }
               
               private void addNumericPoint(TupleData tupleData, Object value) {
                 addDataPoint(tupleData, Long.parseLong((String)value));
               }
               
               private void conditionallyAddNumericPointK(TupleData tupleData, HashMap entryData, String id)
               {
                 if (entryData.containsKey(id)) {
                   addNumericPointK(tupleData, (String)entryData.get(id));
                 } else {
                   TRACE.warning("ID not found:" + id);
                 }
               }
               
               private void addNumericPointK(TupleData tupleData, Object value) {
                 addDataPoint(tupleData, Long.parseLong((String)value) * 1024L);
               }
               
               private void addDataPoint(TupleData tupleData, long value) {
                 tupleData.addDataPoint(value, "");
               }
               
               private void createTuples(Source source, OutputProperties properties)
                 throws GCAndMemoryVisualizerException
               {
                 this.gcNumberConverter = new GCNumberConverter();
                 this.xAxis = VGCAxes.prepareXAxis(source, this.gcNumberConverter, properties);
                 
                 this.heapAxis = VGCAxes.prepareHeapAxis(properties);
                 
                 this.numberAxis = VGCAxes.prepareNumberAxis(properties);
                 this.timeAxis = VGCAxes.prepareTimeAxis(properties);
                 
                 this.unloadedClasses = 
                   createTuple("VGCLabels.classes.unloaded", this.xAxis, this.numberAxis);
                 
                 this.totalTimes = 
                   createPauseTuple("I5V5r4Labels.total.cycle.times", this.xAxis, this.timeAxis);
                 
                 this.markTimes = createPauseTuple("VGCLabels.mark.times", this.xAxis, this.timeAxis);
                 
                 this.sweepTimes = createPauseTuple("VGCLabels.sweep.times", this.xAxis, this.timeAxis);
                 
                 this.compactTimes = 
                   createPauseTuple("VGCLabels.compact.times", this.xAxis, this.timeAxis);
                 
                 this.softReferences = 
                   createTuple("VGCLabels.soft.references.cleared", this.xAxis, this.numberAxis);
                 
                 this.weakReferences = 
                   createTuple("VGCLabels.weak.references.cleared", this.xAxis, this.numberAxis);
                 
                 this.finalReferences = 
                   createTuple("VGCLabels.objects.queued.for.finalization", this.xAxis, this.numberAxis);
                 
                 this.phantomReferences = 
                   createTuple("VGCLabels.phantom.references.cleared", this.xAxis, this.numberAxis);
                 
                 this.usedHeap = 
                   createHeapTuple("VGCLabels.live.normal.heap.after.gc", this.xAxis, this.heapAxis);
                 
                 this.freeHeap = 
                   createHeapTuple("VGCLabels.free.flat.heap", this.xAxis, this.heapAxis);
                 
                 this.heapSize = createHeapTuple("VGCLabels.flat.heap.size", this.xAxis, this.heapAxis);
                 
                 this.freeLOABefore = 
                   createHeapTuple("VGCLabels.free.loa.before.collection", this.xAxis, this.heapAxis);
                 
                 this.freeLOAAfter = 
                   createHeapTuple("VGCLabels.free.loa.after.collection", this.xAxis, this.heapAxis);
                 
                 this.freeSOABefore = 
                   createHeapTuple("VGCLabels.free.soa.before.collection", this.xAxis, this.heapAxis);
                 
                 this.freeSOAAfter = 
                   createHeapTuple("VGCLabels.free.soa.after.collection", this.xAxis, this.heapAxis);
                 
                 this.totalLOABefore = 
                   createHeapTuple("VGCLabels.total.loa.before", this.xAxis, this.heapAxis);
                 
                 this.totalLOAAfter = 
                   createHeapTuple("VGCLabels.total.loa.after", this.xAxis, this.heapAxis);
                 
                 this.totalSOABefore = 
                   createHeapTuple("VGCLabels.total.soa.before", this.xAxis, this.heapAxis);
                 
                 this.totalSOAAfter = 
                   createHeapTuple("VGCLabels.total.soa.after", this.xAxis, this.heapAxis);
                 
                 this.gcInterval = createTuple("VGCLabels.trigger.intervals", this.xAxis, this.timeAxis);
                 
                 TupleMetaData softReferencesMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] {
                   TupleMetaData.TableField.MEAN, TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX, TupleMetaData.TableField.TOTAL });
                 TupleMetaData weakReferencesMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX, TupleMetaData.TableField.TOTAL });
                 TupleMetaData finalReferencesMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX, TupleMetaData.TableField.TOTAL });
                 TupleMetaData phantomReferencesMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX, TupleMetaData.TableField.TOTAL });
                 TupleMetaData totalTimesMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX, TupleMetaData.TableField.TOTAL });
                 TupleMetaData markTimesMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX, TupleMetaData.TableField.TOTAL });
                 TupleMetaData sweepTimesMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX, TupleMetaData.TableField.TOTAL });
                 TupleMetaData compactTimesMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX, TupleMetaData.TableField.TOTAL });
                 TupleMetaData usedHeapMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData freeHeapMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData heapSizeMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData unloadedClassesMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData freeLOABeforeMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData freeLOAAfterMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData freeSOABeforeMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData freeSOAAfterMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData totalLOABeforeMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData totalSOABeforeMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData totalLOAAfterMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData totalSOAAfterMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 TupleMetaData gcIntervalMetaData = new TupleMetaData(
                   TupleMetaData.TableType.ARITHMETIC, new TupleMetaData.TableField[] { TupleMetaData.TableField.MEAN, 
                   TupleMetaData.TableField.MIN, TupleMetaData.TableField.MAX });
                 
                 this.softReferences.setTupleMetaData(softReferencesMetaData);
                 this.weakReferences.setTupleMetaData(weakReferencesMetaData);
                 this.finalReferences.setTupleMetaData(finalReferencesMetaData);
                 this.phantomReferences.setTupleMetaData(phantomReferencesMetaData);
                 this.totalTimes.setTupleMetaData(totalTimesMetaData);
                 this.markTimes.setTupleMetaData(markTimesMetaData);
                 this.sweepTimes.setTupleMetaData(sweepTimesMetaData);
                 this.compactTimes.setTupleMetaData(compactTimesMetaData);
                 this.usedHeap.setTupleMetaData(usedHeapMetaData);
                 this.freeHeap.setTupleMetaData(freeHeapMetaData);
                 this.heapSize.setTupleMetaData(heapSizeMetaData);
                 this.unloadedClasses.setTupleMetaData(unloadedClassesMetaData);
                 this.freeLOABefore.setTupleMetaData(freeLOABeforeMetaData);
                 this.freeLOAAfter.setTupleMetaData(freeLOAAfterMetaData);
                 this.freeSOABefore.setTupleMetaData(freeSOABeforeMetaData);
                 this.freeSOAAfter.setTupleMetaData(freeSOAAfterMetaData);
                 this.totalLOABefore.setTupleMetaData(totalLOABeforeMetaData);
                 this.totalSOABefore.setTupleMetaData(totalSOABeforeMetaData);
                 this.totalLOAAfter.setTupleMetaData(totalLOAAfterMetaData);
                 this.totalSOAAfter.setTupleMetaData(totalSOAAfterMetaData);
                 this.gcInterval.setTupleMetaData(gcIntervalMetaData);
               }
               
               private TupleDataBuilder createTuple(String label, XDataAxis xAxis, YDataAxis yAxis)
               {
                 AxisPair axisPair = factory.createAxisPair(xAxis, yAxis);
                 return factory.createTupleData(label, Messages.getString(label), 
                   axisPair);
               }
               
               private TupleDataBuilder createHeapTuple(String label, XDataAxis xAxis, YDataAxis yAxis)
               {
                 AxisPair axisPair = heapFactory.createAxisPair(xAxis, yAxis);
                 return heapFactory.createTupleData(label, Messages.getString(label), 
                   axisPair);
               }
               
               private TupleDataBuilder createPauseTuple(String label, XDataAxis xAxis, YDataAxis yAxis)
               {
                 AxisPair axisPair = pauseFactory.createAxisPair(xAxis, yAxis);
                 return pauseFactory.createTupleData(label, Messages.getString(label), 
                   axisPair);
               }
               
               private HashMap initIdTable() {
                 HashMap result = new HashMap();
                 result.put(":GC CYCLE NUMBER", "Cycle");
                 result.put("BASIC GC CYCLE INFORMATION", "Basic");
                 result.put("Basic:CURRENT GC CYCLE TIME", "CycleTime");
                 result.put("Basic:GC REASON", "Reason");
                 result.put("Basic:GC AREA", "Area");
                 result.put("Basic:GC COMPACTION REASON", "CompactReason");
                 result.put("Basic:NUMBER OF INTERNAL CYCLES", "InternalCycles");
                 result.put("Basic:TIME SPENT IN EXCESSIVE GC TIME", "ExcessiveTime");
                 result.put("Basic:NUMBER OF OBJECTS MOVED", "ObjectsMoved");
                 result.put("Basic:AMOUNT OF SPACE CONSUMED BY MOVED OBJECTS", 
                   "MovedSpace");
                 
                 result.put("Basic:OBJECTS", "MovedSpace");
                 result.put("Basic:NUMBER OF CLASSES UNLOADED", "ClassesUnloaded");
                 result.put("GC TIME INFORMATION", "Time");
                 result.put("Time:CYCLE START TIME", "CycleStart");
                 result.put("Time:CYCLE END TIME", "CycleEnd");
                 result.put("Time:MARK START TIME", "MarkStart");
                 result.put("Time:MARK END TIME", "MarkEnd");
                 result.put("Time:SWEEP START TIME", "SweepStart");
                 result.put("Time:SWEEP END TIME", "SweepEnd");
                 result.put("Time:COMPACT START TIME", "CompactStart");
                 result.put("Time:COMPACT END TIME", "CompactEnd");
                 result.put("NURSERY AREA INFORMATION", "Nursery");
                 result.put("Nursery:FREE SPACE AT START", "FreeStart");
                 result.put("Nursery:ALLOCATED SPACE AT START", "AllocatedStart");
                 result.put("Nursery:TOTAL SIZE AT START", "TotalStart");
                 result.put("Nursery:FREE SPACE AT END", "FreeEnd");
                 result.put("Nursery:ALLOCATED SPACE AT END", "AllocatedEnd");
                 result.put("Nursery:TOTAL SIZE AT END", "TotalEnd");
                 result.put("TENURED AREA", "Tenured");
                 result.put("Tenured:FREE SPACE AT START", "FreeStart");
                 result.put("Tenured:ALLOCATED SPACE AT START", "AllocatedStart");
                 result.put("Tenured:TOTAL SIZE AT START", "TotalStart");
                 result.put("Tenured:FREE SPACE AT END", "FreeEnd");
                 result.put("Tenured:ALLOCATED SPACE AT END", "AllocatedEnd");
                 result.put("Tenured:TOTAL SIZE AT END", "TotalEnd");
                 result.put("LARGE OBJECT TENURED AREA", "Large");
                 result.put("Large:FREE SPACE AT START", "FreeStart");
                 result.put("Large:ALLOCATED SPACE AT START", "AllocatedStart");
                 result.put("Large:TOTAL SIZE AT START", "TotalStart");
                 result.put("Large:FREE SPACE AT END", "FreeEnd");
                 result.put("Large:ALLOCATED SPACE AT END", "AllocatedEnd");
                 result.put("Large:TOTAL SIZE AT END", "TotalEnd");
                 result.put("SMALL OBJECT TENURED AREA", "Small");
                 result.put("Small:FREE SPACE AT START", "FreeStart");
                 result.put("Small:ALLOCATED SPACE AT START", "AllocatedStart");
                 result.put("Small:TOTAL SIZE AT START", "TotalStart");
                 result.put("Small:FREE SPACE AT END", "FreeEnd");
                 result.put("Small:ALLOCATED SPACE AT END", "AllocatedEnd");
                 result.put("Small:TOTAL SIZE AT END", "TotalEnd");
                 result.put("WEAK OBJECT REFERENCES", "Weak");
                 result.put("Weak:NUMBER AT START", "Start");
                 result.put("Weak:NUMBER AT END", "End");
                 result.put("Weak:NUMBER CLEARED", "Cleared");
                 result.put("FINALIZER OBJECT REFERENCES", "Finalizer");
                 result.put("Finalizer:NUMBER AT START", "Start");
                 result.put("Finalizer:NUMBER AT END", "End");
                 result.put("Finalizer:NUMBER CLEARED", "Cleared");
                 result.put("SOFT OBJECT REFERENCES", "Soft");
                 result.put("Soft:NUMBER AT START", "Start");
                 result.put("Soft:NUMBER AT END", "End");
                 result.put("Soft:NUMBER CLEARED", "Cleared");
                 result.put("PHANTOM OBJECT REFERENCES", "Phantom");
                 result.put("Phantom:NUMBER AT START", "Start");
                 result.put("Phantom:NUMBER AT END", "End");
                 result.put("Phantom:NUMBER CLEARED", "Cleared");
                 
                 return result;
               }
               
               private HashMap parseEntry(BufferedReader reader, OutputProperties properties)
                 throws IOException
               {
                 HashMap result = new HashMap();
                 String section = "";
                 String tag = "";
                 while (!tag.equals("Phantom:Cleared")) {
                   String line = readLine(reader);
                   String rawTag = getRawTag(line);
                   String value = getValue(line);
                   if (value.length() == 0) {
                     if (this.idTable.containsKey(rawTag)) {
                       section = (String)this.idTable.get(rawTag);
                     } else {
                       section = rawTag;
                     }
                   }
                   else {
                     String cookedKey = section + ":" + rawTag;
                     if (this.idTable.containsKey(cookedKey)) {
                       tag = section + ":" + (String)this.idTable.get(cookedKey);
                     } else {
                       tag = cookedKey;
                     }
                     if (section.equals("Time")) {
                       value = Long.toString(convertTime(value));
                     }
                     result.put(tag, value);
                   }
                 }
                 
                 if ((result.containsKey("Time:CycleStart")) && 
                   (result.containsKey("Time:CycleEnd"))) {
                   long start = 
                     Long.parseLong((String)result.get("Time:CycleStart"));
                   long end = Long.parseLong((String)result.get("Time:CycleEnd"));
                   long duration = end - start;
                   result.put("Duration:Cycle", Long.toString(duration));
                 }
                 if ((result.containsKey("Time:MarkStart")) && 
                   (result.containsKey("Time:MarkEnd"))) {
                   long start = 
                     Long.parseLong((String)result.get("Time:MarkStart"));
                   long end = Long.parseLong((String)result.get("Time:MarkEnd"));
                   long duration = end - start;
                   result.put("Duration:Mark", Long.toString(duration));
                 }
                 if ((result.containsKey("Time:SweepStart")) && 
                   (result.containsKey("Time:SweepEnd"))) {
                   long start = 
                     Long.parseLong((String)result.get("Time:SweepStart"));
                   long end = Long.parseLong((String)result.get("Time:SweepEnd"));
                   long duration = end - start;
                   result.put("Duration:Sweep", Long.toString(duration));
                 }
                 if ((result.containsKey("Time:CompactStart")) && 
                   (result.containsKey("Time:CompactEnd"))) {
                   long start = Long.parseLong(
                     (String)result.get("Time:CompactStart"));
                   long end = Long.parseLong((String)result.get("Time:CompactEnd"));
                   long duration = end - start;
                   result.put("Duration:Compact", Long.toString(duration));
                 }
                 if ((result.containsKey("Time:CycleStart")) && 
                   (result.containsKey("Time:SweepEnd"))) {
                   long start = 
                     Long.parseLong((String)result.get("Time:CycleStart"));
                   long end = Long.parseLong((String)result.get("Time:SweepEnd"));
                   long duration = end - start;
                   result.put("Duration:Total", Long.toString(duration));
                 }
                 return result;
               }
               
               private String getRawTag(String line) {
                 int position = line.indexOf(':');
                 position--;
                 while ((line.charAt(position) == ' ') || (line.charAt(position) == '.')) {
                   position--;
                 }
                 return line.substring(0, position + 1);
               }
               
               private String getValue(String line) {
                 int position = line.indexOf(':');
                 position++;
                 while ((position < line.length()) && (line.charAt(position) == ' ')) {
                   position++;
                 }
                 return line.substring(position, line.length());
               }
               
               private long convertTime(String value)
               {
                 if (value.equals("0"))
                   return 0L;
                 Matcher matcher = DATE_PATTERN.matcher(value);
                 matcher.find();
                 int month = Integer.parseInt(matcher.group(1));
                 int day = Integer.parseInt(matcher.group(2));
                 int year = Integer.parseInt(matcher.group(3));
                 int hour = Integer.parseInt(matcher.group(4));
                 int minute = Integer.parseInt(matcher.group(5));
                 int second = Integer.parseInt(matcher.group(6));
                 long ms = Integer.parseInt(matcher.group(7));
                 
                 Calendar cal = Calendar.getInstance();
                 cal.set(year + 2000, month, day, hour, 0, 0);
                 
                 long msFlaky = cal.getTimeInMillis();
                 
                 long hours = (msFlaky + 1800000L) / 3600000L;
                 
                 ms = ((hours * 60L + minute) * 60L + second) * 1000L + ms;
                 return ms;
               }
               
               private String readLine(BufferedReader reader) throws IOException {
                 while (reader.ready()) {
                   String line = reader.readLine();
                   if (line != null) {
                     line = line.trim();
                   }
                   if ((line + " ").indexOf(": ") != -1) {
                     return line.toUpperCase();
                   }
                 }
                 return null;
               }
               
               private boolean tracing() {
                 return TRACE.isLoggable(Level.FINE);
               }
               
               private void trace(String message) {
                 TRACE.log(Level.FINE, message);
               }
             }


